package com.odobenus.client.web;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import javax.annotation.Resource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.odobenus.client.anno.OdobenusFile;
import com.odobenus.client.loader.RefreshPropertySource;
import com.odobenus.client.properties.OdobenusProperties;
import com.odobenus.client.tools.BeanUtils;

@RestController
public class OdobenusController {
	@Resource
	private OdobenusProperties odobenusProperties;
	@Resource
	private ApplicationContext applicationContext;

	@Autowired
	private ConfigurableEnvironment environment;

	@Autowired
	private RefreshPropertySource refreshPropertySource;
	@RequestMapping("fileChange")
	public String fileChange(String app, String version, String env, String fileName) {
		System.out.println(fileName + " changed!");
		String[] beans = applicationContext.getBeanDefinitionNames();
		for (String beanName : beans) {
			// 拿到bean的Class对象
			Class<?> beanType = applicationContext.getType(beanName);
			if (beanType == null) {
				continue;
			}
			// 拿到当前bean类型的所有字段
			Field[] declaredFields = beanType.getDeclaredFields();
			boolean needModify = needModify(declaredFields);
			if (needModify) {
				for (Field declaredField : declaredFields) {
					Class<?> declaredFieldClass = declaredField.getType();
					String fieldName = declaredFieldClass.getSimpleName();
					Object filedBean = applicationContext.getBean(fieldName);
					if (declaredFieldClass.isAnnotationPresent(OdobenusFile.class)) {
						OdobenusFile odobenusFile = declaredFieldClass.getAnnotation(OdobenusFile.class);
						String filePath = odobenusFile.value();
						if (filePath.equals(fileName) && odobenusProperties.getApp().equals(app)
								&& odobenusProperties.getVersion().equals(version)
								&& odobenusProperties.getEnv().equals(env)) {
							if (odobenusProperties.getEnableRemote()) {
								BeanUtils.downloadFile(odobenusProperties, fileName);
							}
							System.out.println("start reload field value");
							Properties props = BeanUtils.load(odobenusProperties, filePath);
							BeanUtils.changeFieldValue(props, filedBean, declaredFieldClass.getDeclaredFields());

							Object bean = applicationContext.getBean(beanName);
							declaredField.setAccessible(true);
							try {
								declaredField.set(bean, filedBean);
								System.out.println("reload success");
							} catch (IllegalArgumentException | IllegalAccessException e) {
								e.printStackTrace();
							}
						}
					}

				}
			}
		}
		return "SUCCESS";
	}
	
	@RequestMapping("fileChangeApollo")
	public String fileChangeApollo(String app, String version, String env, String fileName) {
		System.out.println(fileName + " fileChangeApollo!");
		if (odobenusProperties.getApp().equals(app)
				&& odobenusProperties.getVersion().equals(version) && odobenusProperties.getEnv().equals(env)) {
			if (odobenusProperties.getEnableRemote()) {
				BeanUtils.downloadFile(odobenusProperties, fileName);
				Properties props = BeanUtils.load(odobenusProperties, fileName);
				System.out.println("start reload field value for apollo");
				Map<String, String> map = new HashMap<String, String>((Map) props);
				refreshPropertySource.getSource().putAll(map);
				environment.getPropertySources().addFirst(refreshPropertySource);
				System.out.println(environment.getProperty("app.name"));//实践证明这种方式，这里是刷新了，但是com.odobenus.sample.controller.DemoController.appName字段不会刷新
			}

		}
		return "SUCCESS";
	}

	@RequestMapping("fileChangeApollo2")
	public String fileChangeApollo2(String app, String version, String env, String fileName) {
		System.out.println(fileName + " changed!");
		if ("apollo.properties".equals(fileName) && odobenusProperties.getApp().equals(app)
				&& odobenusProperties.getVersion().equals(version) && odobenusProperties.getEnv().equals(env)) {
			if (odobenusProperties.getEnableRemote()) {
				BeanUtils.downloadFile(odobenusProperties, fileName);
				Properties props = BeanUtils.load(odobenusProperties, fileName);
				System.out.println("start reload field value for apollo");
				//其实这个对于DataSource这种修改后，是不会立即生效的，除了从新产生新的Connection连接
				//printCtx(props);//反射修改内存值，不生效(比如DataSource)，使用热加载技术，这个相当于一个钩子。
			}

		}
		return "SUCCESS";
	}

	private void printCtx(Properties props) {
		String[] beans = applicationContext.getBeanDefinitionNames();
		for (String beanName : beans) {
			// 拿到bean的Class对象
			Class<?> beanType = applicationContext.getType(beanName);
			if (beanType == null) {
				continue;
			}
			// 拿到当前bean类型的所有字段
			Field[] declaredFields = beanType.getDeclaredFields();
			boolean needModify = needModifyApollo(declaredFields);
			if (needModify) {
				for (Field declaredField : declaredFields) {
					Class<?> declaredFieldClass = declaredField.getType();
					String fieldName = declaredField.getName();
					System.out.println(declaredFieldClass+"====="+
							declaredFieldClass.isAnnotationPresent(ConfigurationProperties.class));
					if(declaredFieldClass.equals(DataSourceProperties.class)){
						System.out.println(beanType.getName()+"=========yyyyyyyyyyyyy======"+fieldName);					
						Object filedBean = applicationContext.getBean(DataSourceProperties.class);
						BeanUtils.changeFieldValueApollo(props, filedBean, declaredFieldClass.getDeclaredFields());

						Object bean = applicationContext.getBean(beanName);
						declaredField.setAccessible(true);
						try {
							declaredField.set(bean, filedBean);
							System.out.println("apollo reload success  "+bean.getClass().getName());
							System.out.println(((DataSourceProperties)filedBean).getUrl());
						} catch (IllegalArgumentException | IllegalAccessException e) {
							e.printStackTrace();
						}
					}
					
				}
			}
		}
		
	}

	private boolean needModifyApollo(Field[] declaredFields) {
		for (Field declaredField : declaredFields) {
			Class<?> declaredFieldClass = declaredField.getType();
			if (declaredFieldClass.isAnnotationPresent(ConfigurationProperties.class)) {
				return true;
			}
		}
		return false;
	}

	private boolean needModify(Field[] declaredFields) {
		for (Field declaredField : declaredFields) {
			Class<?> declaredFieldClass = declaredField.getType();
			if (declaredFieldClass.isAnnotationPresent(OdobenusFile.class)) {
				return true;
			}
		}
		return false;
	}

}
